Skip to content

Make component removal best-effort and preserve single-error behavior#4797

Merged
rami3l merged 1 commit intorust-lang:mainfrom
Inconnu08:fix-component-remove-order
Apr 11, 2026
Merged

Make component removal best-effort and preserve single-error behavior#4797
rami3l merged 1 commit intorust-lang:mainfrom
Inconnu08:fix-component-remove-order

Conversation

@Inconnu08
Copy link
Copy Markdown
Contributor

Previously, rustup component remove parsed and removed components in a single pass,
which meant the command stopped on the first error after potentially removing earlier
valid components. As a result, behavior depended on argument order.

This change makes component removal best-effort by:

  • first collecting all parseable requested components
  • then attempting to remove each of them
  • deferring error reporting until the end

This makes the behavior consistent regardless of whether an invalid component appears
before or after valid ones as discussed here: #4794 .

Also adds a regression test covering both orderings.

Closes #4794

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from 21add7c to 9bcc180 Compare April 4, 2026 04:20
Copy link
Copy Markdown
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for working on this :)

View changes since this review

Copy link
Copy Markdown
Contributor

@djc djc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These commits are really not independent -- would prefer to have them all squashed.

View changes since this review

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from 6c5bb6e to aefad2b Compare April 8, 2026 01:42
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 8, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch 3 times, most recently from 7f5f683 to eeb7b06 Compare April 8, 2026 04:15
@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

Hmmm my apologies, but I think I might need some extra time for a final look at this before merging 🙏

@rami3l rami3l self-assigned this Apr 8, 2026
Copy link
Copy Markdown
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm feel okay about it globally but I have a few concerns:

  • I would like to confirm with @FranciscoTGouveia whether the original intention of #4794 is to aggregate certain errors (which is what I believe) rather than just showing the first error but with the bail-out delayed. IMHO the behavior as implemented in this patch will be very confusing to the user if multiple errors do happen. In this case I suggest that we either aggregate UnknownComponent* separately and early bail out for the rest, or actually aggregate all errors (which might be more difficult in terms of display).

  • The current distinction of UnknownComponent and UnknownComponents doesn't seem super necessary since it doesn't prevent the API user from creating the latter with only one Vec member.

  • The tuples in UnknownComponents might be better expressed via a separate struct, but that's a minor implementation detail.

View changes since this review

@Inconnu08
Copy link
Copy Markdown
Contributor Author

Thanks, that makes sense.

My intent with the current implementation was to:

  • preserve the existing UnknownComponent behavior for the single-component case
  • aggregate unknown component names only when multiple invalid components are present
  • keep the rest of the behavior as close as possible to the existing flow

One of the motivations for introducing UnknownComponents was also to avoid repeating the full error message multiple times when several invalid components are provided, which felt a bit noisy UX-wise. The grouped format seemed more concise and easier to read in that case.

That said, I agree the mixed behavior could be confusing if multiple different error types occur. I’m happy to adjust the approach once the intended UX for #4794 is confirmed with @FranciscoTGouveia whether that means aggregating only UnknownComponent* and bailing early for others, or reshaping the error handling more uniformly.

Also, point on UnknownComponent vs UnknownComponents: I introduced the second mainly to avoid regressing the existing single-component behavior, but I’m open to simplifying that if we settle on a cleaner model.

And agreed on the tuple, a small struct would likely make that clearer if we keep the grouped variant.

@FranciscoTGouveia
Copy link
Copy Markdown
Contributor

The original intention of 4794 was indeed to install all possible components and report back every invalid one.

For example, running rustup component add rust-analyzere rustc-dev llvm-toolse, should install rustc-dev successfully, while returning two separate errors, one for each misspelled component.

On another note, I briefly tested the patch and believe it would be worth adding a test case covering the example above, as this is the current output:

$ my-rustup component add rust-analyzere rustc-dev llvm-toolse
error: toolchain '1.90-x86_64-unknown-linux-gnu' does not contain component 'rust-analyzere' for target 'x86_64-unknown-linux-gnu'; did you mean 'rust-analyzer'?

@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

The original intention of 4794 was indeed to install all possible components and report back every invalid one.

@FranciscoTGouveia Thanks for your reply! Given that, my suggestions would be:

  • It'd be easier to just aggregate all UnknownComponent* errors in the installation process and bail out immediately like before if other kinds of errors do occur.

  • On the distinction of UnknownComponent vs UnknownComponents, I'd say it's more idiomatic to special-case the display format on a unified UnknownComponents, rather than doing two enum variants. In other words: if the Vec is of length 1 we print like the old UnknownComponent, otherwise we use the new printing format as introduced here.

  • It'd be better if the helper struct could be introduced.

@Inconnu08 What do you think?

@rami3l rami3l requested a review from FranciscoTGouveia April 8, 2026 19:26
@rami3l rami3l removed their assignment Apr 8, 2026
@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 8, 2026

(Assigned @FranciscoTGouveia for review upon his request.)

@Inconnu08
Copy link
Copy Markdown
Contributor Author

Apologies for the late response. Thanks to both of you for clarifying the intended behavior :)

My understanding now is:

  • we should process all valid components: best-effort
  • aggregate all UnknownComponent* errors and report them together
  • still bail out immediately for other kinds of errors

Using a single UnknownComponents variant with display logic that special-cases the length (to preserve the current single-component/best-match UX) makes sense to me and simplifies the current approach.

The earlier split between UnknownComponent and UnknownComponents was mainly to avoid regressing the single-component formatting/suggestion behavior, but I’m happy to unify it now that the intended direction is clear.

I’ll update the implementation accordingly, and I’ll also:

  • add a test covering the example (rust-analyzere rustc-dev llvm-toolse)
  • extend the existing tests with a case where a valid component appears between invalid ones
  • adjust the error formatting to move suggestions onto a new line for readability

Let me know if that aligns before I push the changes.

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch from eeb7b06 to e626a89 Compare April 11, 2026 04:55
Copy link
Copy Markdown
Member

@rami3l rami3l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's much clearer than before, thanks!

View changes since this review

@Inconnu08 Inconnu08 force-pushed the fix-component-remove-order branch 2 times, most recently from 7d30b9b to 3d561d7 Compare April 11, 2026 18:28
@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 11, 2026

Thanks again @Kobzol , your newly-enabled automations have made the tracking of subtle changes across rebases much easier 🙏

@rami3l
Copy link
Copy Markdown
Member

rami3l commented Apr 11, 2026

@Inconnu08 Great work!

There are some test-related stuff that I'll update for you :)

@rami3l rami3l enabled auto-merge April 11, 2026 18:38
@rami3l rami3l disabled auto-merge April 11, 2026 18:39
@rami3l rami3l force-pushed the fix-component-remove-order branch from 3d561d7 to f798070 Compare April 11, 2026 19:05
@rami3l rami3l force-pushed the fix-component-remove-order branch from f798070 to 64e4d8f Compare April 11, 2026 19:07
@rami3l rami3l enabled auto-merge April 11, 2026 19:11
@rami3l rami3l added this pull request to the merge queue Apr 11, 2026
@rami3l rami3l changed the title Make component removal best-effort and order-independent Make component removal best-effort and preserve single-error behavior Apr 11, 2026
Merged via the queue into rust-lang:main with commit ee831f8 Apr 11, 2026
29 checks passed
@Kobzol
Copy link
Copy Markdown
Member

Kobzol commented Apr 12, 2026

Thanks again @Kobzol , your newly-enabled automations have made the tracking of subtle changes across rebases much easier 🙏

That was actually implemented by @Urgau :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make the removal of components consistent

6 participants